package com.dongrj.framework.core.model;


import com.dongrj.framework.core.constant.Constant;
import com.dongrj.framework.core.exception.BusinessException;
import com.dongrj.framework.core.exception.SystemException;
import lombok.Data;

import java.io.Serializable;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 成功和失败结果用户可见，错误结果用户不可见
 *
 * @author dongrongjun
 * @date 2020/08/04
 */
@Data
public class Result<T> implements Serializable {

    private Boolean success;

    private Integer code;

    private String message;

    private T data;

    private Result() {

    }

    public static <T> Result<T> success(T data) {
        return result(Constant.Code.SUCCESS, data, null);
    }

    public static <T> Result<T> success() {
        return result(Constant.Code.SUCCESS, null, null);
    }

    public static <T> Result<T> fail(String message) {
        return result(Constant.Code.FAIL_BIZ, null, message);
    }

    public static <T> Result<T> error(String message) {
        return result(Constant.Code.FAIL_SYS, null, message);
    }

    private static <T> Result<T> result(Integer code, T data, String message) {
        Result<T> result = new Result<>();
        result.success = Objects.equals(code, Constant.Code.SUCCESS);
        result.data = data;
        result.code = code;
        result.message = message;
        return result;
    }

    public T checkData(Supplier<T> tSupplier) {
        if (success) {
            if (data == null) {
                if (tSupplier == null) {
                    return null;
                } else {
                    return tSupplier.get();
                }
            }
            return data;
        }
        if (Objects.equals(code, Constant.Code.FAIL_BIZ)) {
            throw new BusinessException(message);
        }
        if (Objects.equals(code, Constant.Code.FAIL_SYS)) {
            throw new SystemException(message);
        }
        throw new SystemException(message);
    }

    public T checkData() {
        return checkData(null);
    }

    public static <T, E> E map(Result<T> r, Function<T, E> function, E defaultE) {
        if (r == null) {
            return defaultE;
        }
        T data = r.getData();
        if (data == null) {
            return defaultE;
        }
        E apply = function.apply(data);
        if (apply == null) {
            return defaultE;
        }
        return apply;
    }
}
